.text
.code32 // Assembly generates 32 bit machine instructions
.start32:
  mov $0x10, %ax // Kernel data segment selector
  mov %ax, %ds
  mov %ax, %es
 
  lgdt gdtr // Notify processor of segment descriptor table address
 
// Loop fill 32 page table entries into the second level page table: mapping 32 first level page tables
  mov $0x32000, %edi // 0x20000 + 0x12000 = 0x32000 starting address of Second level page table
  mov $0x33000 + 3, %eax // page table entry value: mapping 1 first level page table
1:
  stosl // Write %eax to %edi and increase $edi by 4B
  add $0x1000, %eax // increase by 4KB, representing 1 page
  add $4, %edi // increase by 4B, total 8B, representing 1 table entry
 
  cmp $(0x32000 + 31 * 8), %edi
  jle 1b
 
// Loop fill 32 first page tables: mapping 32 physical page bases
  mov $0x33000, %edi // 0x20000 + 0x13000 = 0x33000 starting address of first level page table
  mov $0x0 + 3, %eax // page table entry value: mapping 1 physical page base
2:
  stosl // Write %eax to %edi and increase $edi by 4B
  add $0x1000, %eax // increase by 4KB, representing 1 page
  add $4, %edi // increase by 4B, total 8B, representing 1 table entry
 
  cmp $(0x33000 + 512 * 32 * 8 - 8), %edi
  jle 2b

// Loop fill 32 page table entries into the second level page table: mapping old 32 first level page tables
  mov $0x56000, %edi // 0x20000 + 0x36000 = 0x56000 starting address of first level page table
  mov $0x33000 + 3, %eax // page table entry value: mapping 1 physical page base
3:
  stosl // Write %eax to %edi and increase $edi by 4B
  add $0x1000, %eax // increase by 4KB, representing 1 page
  add $4, %edi // increase by 4B, total 8B, representing 1 table entry

  cmp $(0x56000 + 32 * 8 - 8), %edi
  jle 3b
  
  // Enable PAE expand 32-bit physical address
  mov %cr4, %eax
  btsl $5, %eax
  mov %eax, %cr4
 
  // Set cr3 to point to page table
  movl $0x30000, %eax // Fourth level page table base 0x10000+0x20000
  movl %eax, %cr3
 
  // Enable 64 bit mode
  mov $0xc0000080, %ecx
  rdmsr
  bts $8, %eax
  bts $0, %eax
  wrmsr
 
  // Enable pagination
  mov %cr0, %eax
  bts $31, %eax
  mov %eax, %cr0
 
  ljmp $0x8, $0x100000 // Jump to the 64-bit mode code segment section
 
gdt: // Definition of segment descriptor table
  .quad 0x0000000000000000 // 0th descriptor - No need, x86 convention is 0
  .quad 0x00209a0000000000 // 1th descriptor - kernel code segment
  .quad 0x0000920000000000 // 2th descriptor - kernel data segment
gdt_end:

gdtr: // Segment descriptor table address definition, total of 6B
  .word gdt_end - gdt // Low 16 bits - Table length - Starting and ending offset difference of 2B
  .long gdt // High 32-bit - Table Address 4B
 
.org 0x10000 // Fourth level page table: starting address - a 4KB page table
  .quad 0x0000000000053003 // [identity map] 0th page table entry maps to the base address of the new third level page table 0
  .fill 272, 8, 0 // remaining page table entries set to 0
  .quad 0x0000000000055003 // [linear map] 273th page table entry maps to the base address of another new third level page table
  .fill 237, 8, 0 // remaining page table entries set to 0
  .quad 0x0000000000031003 // 0th to last page table entry maps to the base address of the third level page table
 
.org 0x11000 // Third level page table: starting address - a 4KB page table
  .fill 510, 8, 0 // remaining page table entries set to 0
  .quad 0x0000000000032003 // 1th to last page table entry maps to the base address of the second level page table
  .fill 1, 8, 0 // remaining page table entries set to 0
 
.org 0x12000 // Second level page table: starting address - a 4KB page table
  .fill 512, 8, 0 // init all page table entries to 0
 
.org 0x13000 // First level page table: starting address - 32 4KB page tables
  .fill 512 * 32, 8, 0 // init all page table entries to 0
 
.org 0x33000 // [identity map] New third level page table: starting address - a 4KB page table
  .quad 0x0000000000054003 // [identity map] 0th page table entry maps to the base address of the new second level page table 0
  .fill 511, 8, 0 // init all page table entries to 0

.org 0x34000 // [identity map] New second level page table: starting address - a 4KB page table
  .quad 0x0000000000033003 // [identity map] 0th page table entry maps to the base address of the first level page table 0
  .fill 511, 8, 0 // init all page table entries to 0

.org 0x35000 // [linear map] New third level page table: starting address - a 4KB page table
  .quad 0x0000000000056003 // [linear map] 0th page table entry maps to the base address of the second level page table
  .fill 511, 8, 0 // init all page table entries to 0

.org 0x36000 // [linear map] New second level page table: starting address - a 4KB page table
  .fill 32, 8, 0 // mapping old 32 first level page tables
  .quad 0x0000000000057003 // [linear map] 0th page table entry maps to the base address of the first level page table
  .fill 479, 8, 0 // init all page table entries to 0

.org 0x37000 // [linear map] New first level page table: starting address - a 4KB page table
  .fill 512, 8, 0 // init all page table entries to 0